
<template>
  <div class="cube">
     <!-- <canvas>标记提供webgl绘制区域 -->
    <canvas ref="myGLCanvas" :width="rect?.width" :height="rect?.height"/>
  </div>
</template>

<script lang="ts">
import { Vue, Options } from 'vue-property-decorator';
import { mat4 } from 'gl-matrix';
@Options({ components: {} })
export default class Cube extends Vue {
  public rect: DOMRect|null = null;
  public gl!: WebGLRenderingContext;
  public shaderProgram: WebGLProgram|null=null;
  public floorVertexPositonBuffer: WebGLBuffer|null = null;
  public floorVertexIndexBuffer: WebGLBuffer|null = null;
  public cubeVertexPositionBuffer: WebGLBuffer|null = null;
  public cubeVertexIndexBuffer: WebGLBuffer|null = null;
  public cubeVertexColorBuffer: WebGLBuffer|null = null;
  public vertexPositionAttribute=0;
  public vertexColorAttribute=0;
  public modelViewMatrix!: mat4;
  public projectionMatrix!: mat4;
  public uniformMVMatrix!: WebGLUniformLocation;
  public uniformProjectMatrix!: WebGLUniformLocation;
  public squareRotation=0;


  public drawCube() {
    // 通过参数this.gl.COLOR_BUFFER_BIT把颜色缓冲清除为事先用 this.gl.clearColor()定义的颜色
    this.gl.clear(this.gl.COLOR_BUFFER_BIT | this.gl.DEPTH_BUFFER_BIT);

    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer);
    this.gl.vertexAttribPointer(this.vertexPositionAttribute, 3, this.gl.FLOAT, false, 0, 0);
    this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.cubeVertexIndexBuffer);
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexColorBuffer);
    this.gl.vertexAttribPointer(this.vertexColorAttribute, 4, this.gl.FLOAT, false, 0, 0);
    this.setupTransformMatrix();
    this.gl.drawElements(this.gl.TRIANGLES, 36, this.gl.UNSIGNED_SHORT, 0);
    requestAnimationFrame(() => {
      this.drawCube();
    });
  }
  // 4.设置变换矩阵
  public setupTransformMatrix() {
    // 创建模型视图矩阵 投影矩阵
    this.modelViewMatrix = mat4.create();
    this.projectionMatrix = mat4.create();
    // 在将场景中的模型视图矩阵变换应用于模型视图矩阵之前对它初始化
    mat4.identity(this.modelViewMatrix);
    mat4.lookAt(this.modelViewMatrix, [15, 15, 15], [0, 0, 0], [0, 1, 0]);
    // mat4.translate(this.modelViewMatrix, this.modelViewMatrix, [0.0, 3.0, 0.0]);
    // 第二个参数变化？？？？？？？？？？
    this.squareRotation += 0.01;
    mat4.rotate(this.modelViewMatrix, this.modelViewMatrix, this.squareRotation, [0, 0, 1]);
    mat4.perspective(this.projectionMatrix, 70, this.rect!.width / this.rect!.height, 0.1, 100.0);

    // 在变换矩阵作用于顶点着色器中的顶点之前将其上传到GPU
    this.gl.uniformMatrix4fv(this.uniformMVMatrix, false, this.modelViewMatrix);
    this.gl.uniformMatrix4fv(this.uniformProjectMatrix, false, this.projectionMatrix);
  }
  // 3.建立缓冲，用来保存顶点数据
  public setupBuffers() {
    this.cubeVertexPositionBuffer = this.gl.createBuffer();
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexPositionBuffer);
    const cubeVertexPosition = [
      // Front face
      -1.0, -1.0,  1.0,
      1.0, -1.0,  1.0,
      1.0,  1.0,  1.0,
      -1.0,  1.0,  1.0,

      // Back face
      -1.0, -1.0, -1.0,
      -1.0,  1.0, -1.0,
      1.0,  1.0, -1.0,
      1.0, -1.0, -1.0,

      // Top face
      -1.0,  1.0, -1.0,
      -1.0,  1.0,  1.0,
      1.0,  1.0,  1.0,
      1.0,  1.0, -1.0,

      // Bottom face
      -1.0, -1.0, -1.0,
      1.0, -1.0, -1.0,
      1.0, -1.0,  1.0,
      -1.0, -1.0,  1.0,

      // Right face
      1.0, -1.0, -1.0,
      1.0,  1.0, -1.0,
      1.0,  1.0,  1.0,
      1.0, -1.0,  1.0,

      // Left face
      -1.0, -1.0, -1.0,
      -1.0, -1.0,  1.0,
      -1.0,  1.0,  1.0,
      -1.0,  1.0, -1.0,
    ];
    this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(cubeVertexPosition), this.gl.STATIC_DRAW); // 3 24

    this.cubeVertexIndexBuffer = this.gl.createBuffer();
    this.gl.bindBuffer(this.gl.ELEMENT_ARRAY_BUFFER, this.cubeVertexIndexBuffer);
    const cubeVertexIndex = [
      0,  1,  2,      0,  2,  3,    // front
      4,  5,  6,      4,  6,  7,    // back
      8,  9,  10,     8,  10, 11,   // top
      12, 13, 14,     12, 14, 15,   // bottom
      16, 17, 18,     16, 18, 19,   // right
      20, 21, 22,     20, 22, 23,    // left
    ];
    this.gl.bufferData(this.gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndex), this.gl.STATIC_DRAW);// 1 36

    this.cubeVertexColorBuffer = this.gl.createBuffer();
    this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexColorBuffer);
    // 前红 后绿 上蓝 下黄 右浅蓝 左紫
    const colors = [
      [1.0, 0.0, 0.0, 1.0],
      [0.0, 1.0, 0.0, 1.0],
      [0.0, 0.0, 1.0, 1.0],
      [1.0, 1.0, 0.0, 1.0],
      [0.0, 1.0, 1.0, 1.0],
      [1.0, 0.0, 1.0, 1.0],
    ];
    let cubeVertexColor: number[] = [];
    for (let i = 0;i < 6;i++) {
      const c = colors[i];
      for (let j = 0;j < 4;j++) {
        cubeVertexColor = cubeVertexColor.concat(c);
      }
    }
    this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(cubeVertexColor), this.gl.STATIC_DRAW);

    this.gl.enableVertexAttribArray(this.vertexPositionAttribute); // 打开属性数组列表中指定索引处的通用顶点属性数组 不用重复激活 一次就可以
    this.gl.enableVertexAttribArray(this.vertexColorAttribute); // 打开属性数组列表中指定索引处的通用顶点属性数组 不用重复激活 一次就可以
  }
  public setupShaders() {
    const vertexShaderSource = `precision mediump float;
                                attribute vec3 aVertexPosition;
                                attribute vec4 aVertexColor;
                                uniform mat4 uMVMatrix;
                                uniform mat4 uPMatrix;
                                varying vec4 vColor;
                                void main(){
                                  gl_Position = uPMatrix * uMVMatrix * vec4(aVertexPosition,1.0);
                                  vColor = aVertexColor;
                                }`;
    const fragmentShaderSource = `precision mediump float;
                                  varying vec4 vColor;
                                  void main(){     
                                    gl_FragColor = vColor;
                                  }`;
    const vertexShader = this.loadShader(this.gl.VERTEX_SHADER, vertexShaderSource);
    const fragmentShader = this.loadShader(this.gl.FRAGMENT_SHADER, fragmentShaderSource);
    this.shaderProgram = this.gl.createProgram();
    this.gl.attachShader(this.shaderProgram!, vertexShader!);
    this.gl.attachShader(this.shaderProgram!, fragmentShader!);
    this.gl.linkProgram(this.shaderProgram!);
    if (!this.gl.getProgramParameter(this.shaderProgram!, this.gl.LINK_STATUS)) {
      alert('Failed to setup shaders');
    }
    this.gl.useProgram(this.shaderProgram!);
    this.vertexPositionAttribute = this.gl.getAttribLocation(this.shaderProgram!, 'aVertexPosition');
    this.vertexColorAttribute = this.gl.getAttribLocation(this.shaderProgram!, 'aVertexColor');
    this.uniformMVMatrix = this.gl.getUniformLocation(this.shaderProgram!, 'uMVMatrix')!;
    this.uniformProjectMatrix = this.gl.getUniformLocation(this.shaderProgram!, 'uPMatrix')!;
  }
  public loadShader(type: number, shaderSource: any) {
    const shader = this.gl.createShader(type);
    this.gl.shaderSource(shader!, shaderSource);
    this.gl.compileShader(shader!);
    if (!this.gl.getShaderParameter(shader!, this.gl.COMPILE_STATUS)) {
      alert(`Error Compiling shader${this.gl.getShaderInfoLog(shader!)}`);
      this.gl.deleteShader(shader);
      return null;
    }
    return shader;
  }
  public mounted() {
    this.rect = (this.$el as HTMLDivElement).getBoundingClientRect();
    this.gl = (this.$refs.myGLCanvas as HTMLCanvasElement).getContext('webgl') as WebGLRenderingContext;
    this.setupShaders();
    this.setupBuffers();
    this.gl.enable(this.gl.DEPTH_TEST);
    this.gl.clearColor(0, 0, 0, 1); // 用于设置清空颜色缓冲时的颜色值
    this.gl.viewport(0, 0, this.rect!.width, this.rect!.height);// 定义一个视口
    this.drawCube();
  }
}
</script>

<style lang="postcss" scoped>
.cube{
    touch-action: none;
    position: absolute;
    left: 0;top: 0;
    width: 100%;
    height: 100%;
    background-color: grey;
}
</style>
